# 访问数据库
# 程序运行的时候，数据都是在内存中的。当程序终止的时候，通常都需要将数据保存到磁盘上，无论是保存到本地磁盘，
# 还是通过网络保存到服务器上，最终都会将数据写入磁盘文件。
# 用文本文件保存,或者json格式保存的问题：
# 1.存储和读取需要自己实现，JSON还是标准，自己定义的格式就各式各样了；
# 2.不能做快速查询，只有把数据全部读到内存中才能自己遍历，但有时候数据的大小远远超过了内存
# （比如蓝光电影，40GB的数据），根本无法全部读入内存。
# 为了便于程序保存和读取数据，而且，能直接通过条件快速查询到指定的数据，
# 就出现了数据库（Database）这种专门用于集中存储和查询的软件。
# 数据库软件诞生的历史非常久远，早在1950年数据库就诞生了。经历了网状数据库，层次数据库，
# 我们现在广泛使用的关系数据库是20世纪70年代基于关系模型的基础上诞生的。
# NoSQL :  你也许还听说过NoSQL数据库，很多NoSQL宣传其速度和规模远远超过关系数据库，
# 所以很多同学觉得有了NoSQL是否就不需要SQL了呢？千万不要被他们忽悠了，连SQL都不明白怎么可能搞明白NoSQL呢？
# 付费的商用数据库：
# Oracle，典型的高富帅；
# SQL Server，微软自家产品，Windows定制专款；
# DB2，IBM的产品，听起来挺高端；
# Sybase，曾经跟微软是好基友，后来关系破裂，现在家境惨淡。
# 免费的开源数据库：
# MySQL，大家都在用，一般错不了；
# PostgreSQL，学术气息有点重，其实挺不错，但知名度没有MySQL高；
# sqlite，嵌入式数据库，适合桌面和移动应用。
# 作为Python开发工程师，选择哪个免费数据库呢？当然是MySQL。因为MySQL普及率最高，出了错，可以很容易找到解决方法。
# 而且，围绕MySQL有一大堆监控和运维的工具，安装和使用很方便。
# 为了能继续后面的学习，你需要从MySQL官方网站下载并安装MySQL Community Server 5.6，这个版本是免费的，其他高级版本是要收钱的
# https://dev.mysql.com/downloads/mysql/
# https://www.jb51.net/article/147682.htm  # 安装教程

# 1. 使用SQLite
# SQLite是一种嵌入式数据库，它的数据库就是一个文件。由于SQLite本身是C写的，而且体积很小，所以，经常被集成到各种应用程序中，甚至在iOS和Android的App中都可以集成。
# Python就内置了SQLite3，所以，在Python中使用SQLite，不需要安装任何东西，直接使用。

import sqlite3, os

# test.db为数据库文件,不存在时会自动创建
dirPath = os.path.join('.', 'file')
filePath = os.path.join(dirPath, 'test.db')


def insert():
    # 连接数据库
    conn = sqlite3.connect(filePath)
    # 创建一个cursor
    cursor = conn.cursor()
    # 创建表
    cursor.execute('create table user (id varchar(20) primary key,name varchar(20))')
    # 插入数据
    cursor.execute('insert into user (id,name ) values (\'1\',\'ljy\')')
    cursor.execute('insert into user (id,name ) values (\'2\',\'bob\')')
    # 获得行数
    print('rowcount:', cursor.rowcount)
    # 关闭cursor
    cursor.close()
    # 提交事务
    conn.commit()
    # 关闭连接
    conn.close()


def query():
    conn = sqlite3.connect(filePath)
    cursor = conn.cursor()
    # 执行查询
    cursor.execute('select * from user')
    # 如果SQL语句带有参数，那么需要把参数按照位置传递给execute()方法，有几个?占位符就必须对应几个参数
    # cursor.execute('select * from user where id=?', ('1',))
    values = cursor.fetchall()
    print(values)
    cursor.close()
    conn.close()


# if __name__ == '__main__':
# insert()
# query()


# 2. 使用MySQL
# MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入，但不能承受高并发访问，适合桌面和移动应用。
# 而MySQL是为服务器端设计的数据库，能承受高并发访问，同时占用的内存也远远大于SQLite。
# 此外，MySQL内部有多种数据库引擎，最常用的引擎是支持数据库事务的InnoDB。
# 安装MySQL驱动
# pip install mysql-connector-python --allow-external mysql-connector-python

import mysql.connector


def insert_mysql():
    # 连接
    conn = mysql.connector.connect(user='root', password='123456', database='test')
    cursor = conn.cursor()
    cursor.execute('create table user (id varchar(20) primary key, name varchar(20))')
    cursor.execute('insert into user (id,name ) values(%s,%s)', ['1', 'jinyang'])
    cursor.execute('insert into user (id,name ) values(%s,%s)', ['2', 'alan'])
    print(cursor.rowcount)
    conn.commit()
    cursor.close()
    conn.close()


def query_mysql():
    conn = mysql.connector.connect(user='root', password='123456', database='test')
    cursor = conn.cursor()
    cursor.execute('select * from user')
    values = cursor.fetchall()
    print(values)
    cursor.close()
    conn.close()


# if __name__ == '__main__':
#     insert_mysql()
#     query_mysql()


# 3. 使用SQLAlchemy
# 数据库表是一个二维表，包含多行多列。把一个表的内容用Python的数据结构表示出来的话，可以用一个list表示多行，list的每一个元素是tuple，表示一行记录，
# 但是用tuple表示一行很难看出表的结构。如果把一个tuple用class实例来表示，就可以更容易地看出表的结构来
# 这就是传说中的ORM技术：Object-Relational Mapping，把关系数据库的表结构映射到对象上。是不是很简单
# 所以ORM框架应运而生。在Python中，最有名的ORM框架是SQLAlchemy。
# 安装pip install sqlalchemy
from sqlalchemy import Column, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# 创建对象的基类:
Base = declarative_base()


# 定义User对象:
class User(Base):
    # 表的名字:
    __tablename__ = 'user'

    # 表的结构:
    id = Column(String(20), primary_key=True)
    name = Column(String(20))


# 初始化数据库连接:
# create_engine()用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息：
#   '数据库类型+数据库驱动名称://用户名:口令@机器地址:端口号/数据库名'
engine = create_engine('mysql+mysqlconnector://root:123456@localhost:3306/test')
# 创建DBSession类型:
DBSession = sessionmaker(bind=engine)


def orm_insert():
    # 创建session对象:
    session = DBSession()
    # 创建新User对象:
    new_user = User(id='5', name='Bob')
    # 添加到session:
    session.add(new_user)
    # 提交即保存到数据库:
    session.commit()
    # 关闭session:
    session.close()


def orm_query():
    # 创建Session:
    session = DBSession()
    # 创建Query查询，filter是where条件，最后调用one()返回唯一行，如果调用all()则返回所有行:
    user = session.query(User).filter(User.id == '5').one()
    # user = session.query(User).filter(User.id == '1').one()
    # 打印类型和对象的name属性:
    print('type:', type(user))
    print('name:', user.name)
    # 关闭Session:
    session.close()


if __name__ == '__main__':
    # orm_insert()
    orm_query()

# 由于关系数据库的多个表还可以用外键实现一对多、多对多等关联，相应地，ORM框架也可以提供两个对象之间的一对多、多对多等功能。
#
# 例如，如果一个User拥有多个Book，就可以定义一对多关系如下：
#
# class User(Base):
#     __tablename__ = 'user'
#
#     id = Column(String(20), primary_key=True)
#     name = Column(String(20))
#     # 一对多:
#     books = relationship('Book')
#
# class Book(Base):
#     __tablename__ = 'book'
#
#     id = Column(String(20), primary_key=True)
#     name = Column(String(20))
#     # “多”的一方的book表是通过外键关联到user表的:
#     user_id = Column(String(20), ForeignKey('user.id'))
# 当我们查询一个User对象时，该对象的books属性将返回一个包含若干个Book对象的list。
